package com.common.base.app

import android.app.Application
import android.content.Context
import android.content.Intent
import android.net.http.HttpResponseCache
import android.util.Log
import androidx.multidex.BuildConfig
import androidx.multidex.MultiDex
import com.alibaba.android.arouter.launcher.ARouter
import com.common.base.app.extras.otherwise
import com.common.base.app.extras.yes
import com.common.base.config.AppConfig
import com.common.base.service.X5ProcessInitService
import com.common.base.utils.AppUtils
import com.common.base.utils.DebugUtils
import com.common.base.utils.LogUtils
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.decoder.ProgressiveJpegConfig
import com.facebook.imagepipeline.image.ImmutableQualityInfo
import com.facebook.imagepipeline.image.QualityInfo
import com.facebook.imagepipeline.transcoder.SimpleImageTranscoderFactory
import com.facebook.imageutils.BitmapUtil
import com.jeremyliao.liveeventbus.LiveEventBus
import com.tencent.bugly.crashreport.CrashReport
import com.tencent.mmkv.MMKV
import com.tencent.smtt.export.external.TbsCoreSettings
import com.tencent.smtt.sdk.QbSdk
import com.tencent.smtt.sdk.WebView
import me.yokeyword.fragmentation.Fragmentation
import java.io.File

/**
 * 作者：sosou
 * <p>
 * 版本：1.0
 * <p>
 * 创建日期：2022/8/19
 * <p>
 * 描述：
 * <p>
 * 修订历史：
 */
abstract class BaseApplication : Application() {

    companion object {
        private lateinit var mApplication: BaseApplication
        fun getApplication(): BaseApplication {
            return mApplication
        }
    }

    override fun onCreate() {
        super.onCreate()
        mApplication = this
        initBaseMainProcess()
        initMainProcess()
    }

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(base)
    }

    /**
     * 父类自己初始化
     * 运行在主线程
     */
    private fun initBaseMainProcess() {
        initMMKV()
        initEnv()
        initBugly()
        initARouter()
        initLiveBus()
        initFragmentation()
        initTbs()
        initPicture()
    }

    private fun initTbs() {
        // 在调用TBS初始化、创建WebView之前进行如下配置
        val map: HashMap<String, Any> = HashMap()
        map[TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER] = true
        map[TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE] = true
        QbSdk.initTbsSettings(map)

//        if (!startX5WebProcessPreinitService()) return
//        /* 设置允许移动网络下进行内核下载。默认不下载，会导致部分一直用移动网络的用户无法使用x5内核 */
//        QbSdk.setCoreMinVersion(QbSdk.CORE_VER_ENABLE_202112)
//
//        /* SDK内核初始化周期回调，包括 下载、安装、加载 */
//        QbSdk.setTbsListener(object : TbsListener {
//            /**
//             * @param stateCode 用户可处理错误码请参考[com.tencent.smtt.sdk.TbsCommonCode]
//             */
//            override fun onDownloadFinish(stateCode: Int) {
//                Log.i(
//                    "com.tencent.tbs.demo.DemoApplication.TAG",
//                    "onDownloadFinished: $stateCode"
//                )
//            }
//
//            /**
//             * @param stateCode 用户可处理错误码请参考[com.tencent.smtt.sdk.TbsCommonCode]
//             */
//            override fun onInstallFinish(stateCode: Int) {
//                Log.i(
//                    "com.tencent.tbs.demo.DemoApplication.TAG",
//                    "onInstallFinished: $stateCode"
//                )
//            }
//
//            /**
//             * 首次安装应用，会触发内核下载，此时会有内核下载的进度回调。
//             * @param progress 0 - 100
//             */
//            override fun onDownloadProgress(progress: Int) {
//                Log.i("com.tencent.tbs.demo.DemoApplication.TAG", "Core Downloading: $progress")
//            }
//        })
//
        /* 此过程包括X5内核的下载、预初始化，接入方不需要接管处理x5的初始化流程，希望无感接入 */
        QbSdk.initX5Environment(
            this,
            object : QbSdk.PreInitCallback {
                override fun onCoreInitFinished() {
                    // 内核初始化完成，可能为系统内核，也可能为系统内核
                }

                /**
                 * 预初始化结束
                 * 由于X5内核体积较大，需要依赖wifi网络下发，所以当内核不存在的时候，默认会回调false，此时将会使用系统内核代替
                 * 内核下发请求发起有24小时间隔，卸载重装、调整系统时间24小时后都可重置
                 * 调试阶段建议通过 WebView 访问 debugtbs.qq.com -> 安装线上内核 解决
                 * @param isX5 是否使用X5内核
                 */
                override fun onViewInitFinished(isX5: Boolean) {
                    Log.i("com.tencent.tbs.demo.DemoApplication.TAG", "onViewInitFinished: $isX5")
                    // hint: you can use QbSdk.getX5CoreLoadHelp(context) anytime to get help.

                    if (DebugUtils.isDebugModel()){//线上环境注释调
                        WebView.setWebContentsDebuggingEnabled(true)
                    }
                }
            })
    }

    private fun initARouter() {
        if (DebugUtils.isDebugModel()) {     // These two lines must be written before init, otherwise these configurations will be invalid in the init process
            ARouter.openLog()     // Print log
            ARouter.openDebug()   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
        }
        ARouter.init(this) // As early as possible, it is recommended to initialize in the Application
    }

    private fun initBugly() {
        val strategy = CrashReport.UserStrategy(this)
        strategy.appChannel = AppUtils.getChannel()//设置渠道
        strategy.appVersion = AppUtils.getVersionName()//App的版本
        strategy.appPackageName = AppUtils.getPackageName()//App的包名
        strategy.deviceModel = AppUtils.getDeviceModel()//设备名
//        strategy.deviceID = getUtdid(this)//设备名
        strategy.setCrashHandleCallback(object: CrashReport.CrashHandleCallback(){
            override fun onCrashHandleStart(crashType: Int, errorType: String?, errorMessage: String?, errorStack: String?): MutableMap<String, String> {
                val map = LinkedHashMap<String, String>()
                val x5CrashInfo: String = WebView.getCrashExtraMessage(this@BaseApplication)
                map["x5crashInfo"] = x5CrashInfo
                return map
            }

            override fun onCrashHandleStart2GetExtraDatas(crashType: Int, errorType: String?, errorMessage: String?, errorStack: String?): ByteArray? {
                return try {
                    "Extra data.".toByteArray(charset("UTF-8"))
                } catch (e: Exception) {
                    null
                }
            }
        })
        DebugUtils.isReleaseModel().yes {
            CrashReport.initCrashReport(this, AppConfig.BUGLY_KEY, false, strategy)
        }.otherwise {
            CrashReport.initCrashReport(this, AppConfig.BUGLY_KEY_DEV, false, strategy)
        }
        CrashReport.setAppChannel(this, AppUtils.getChannel())
    }

    //MMKV 缓存
    private fun initMMKV() {
        MMKV.initialize(this)
    }

    //图片框架配置
    private fun initPicture() {
        Fresco.initialize(AppUtils.getApplication())
//        //下采样
//        val imagePipelineConfig = ImagePipelineConfig.newBuilder(AppUtils.getApplication())
//            .setProgressiveJpegConfig(jpegConfig)
//            .setImageTranscoderFactory(SimpleImageTranscoderFactory(BitmapUtil.MAX_BITMAP_SIZE.toInt()))
//            .setDownsampleEnabled(true)
//            .build()
//        Fresco.initialize(AppUtils.getApplication(),imagePipelineConfig)
        //SVGA缓存
        val cacheDir = File(AppUtils.getApplication().cacheDir, "http")
        HttpResponseCache.install(cacheDir, 1024 * 1024 * 128)
    }

    //加载质量配置,为了实现节省GPU(图形处理器),随着图片的下载,下完的扫描序列:1、4、5、10(固定)设置渐进式JPEG Config
    private var jpegConfig: ProgressiveJpegConfig = object : ProgressiveJpegConfig {
        override fun decodeProgressively(): Boolean {
            return true
        }

        //下次在该扫描次数时候进行解码，初始为0
        override fun getNextScanNumberToDecode(scanNumber: Int): Int {
            return scanNumber + 2
        }
        //获取当前扫描次数的质量信息
        //这里的意思是，当扫描到第五次的时候就返回该信息
        //ImmutableQualityInfo.of(scanNumber,isGoodEnough,isFullQuality)
        //param1：扫描次数；param2：是否已足够；param3:是否是完整图片质量
        override fun getQualityInfo(scanNumber: Int): QualityInfo {
            val isGoodEnough = scanNumber >= 5
            return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false)
        }
    }

    private fun initLiveBus() {
        LiveEventBus
            .config()
            .autoClear(true)
            .enableLogger(false)
            .lifecycleObserverAlwaysActive(true)
    }

    private fun initFragmentation() {
        Fragmentation.builder()
            // 设置 栈视图 模式为 （默认）悬浮球模式   SHAKE: 摇一摇唤出  NONE：隐藏， 仅在Debug环境生效
            .stackViewMode(Fragmentation.NONE)
//            .debug(DebugUtils.isDevModel())
            .debug(BuildConfig.DEBUG)
            .handleException {
                LogUtils.e(it)
            }
            .install()
    }


    /**
     * 初始化环境
     */
    private fun initEnv() {
        DebugUtils.setEnvLog(isEnvLog())
        DebugUtils.setEnvShow(isEnvShow())
        DebugUtils.setCurrentEnvModel(getCurrentEnvModel())
    }

    /**
     * 启动X5 独立Web进程的预加载服务。优点：
     * 1、后台启动，用户无感进程切换
     * 2、启动进程服务后，有X5内核时，X5预加载内核
     * 3、Web进程Crash时，不会使得整个应用进程crash掉
     * 4、隔离主进程的内存，降低网页导致的App OOM概率。
     *
     * 缺点：
     * 进程的创建占用手机整体的内存，demo 约为 150 MB
     */
    private fun startX5WebProcessPreinitService(): Boolean {
        val currentProcessName = QbSdk.getCurrentProcessName(this)
        // 设置多进程数据目录隔离，不设置的话系统内核多个进程使用WebView会crash，X5下可能ANR
        WebView.setDataDirectorySuffix(QbSdk.getCurrentProcessName(this))
        Log.i("X5WebProcessPreinitService-TAG", currentProcessName)
        if (currentProcessName == this.packageName) {
            startService(Intent(this, X5ProcessInitService::class.java))
            return true
        }
        return false
    }


    /**
     * 是否可以打印日志
     */
    abstract fun isEnvLog(): Boolean

    /**
     * 显示切换环境开关
     */
    abstract fun isEnvShow(): Boolean

    /**
     * 在主线程初始化子application
     */
    abstract fun initMainProcess()

    /**
     * 获取当前的开发环境模式
     */
    abstract fun getCurrentEnvModel(): Int
}